#ifndef INTERFACE_H
#define INTERFACE_H

#define interface struct

//===========================================================================
struct Piece
{
    enum EType {
        NONE  = 0,
        BLACK = 1,
        WHITE = 2,
        
        COUNT
    };

    EType type;

    /* explicit */ Piece(
        const EType & type
    )   : type( type )
    {}

    Piece( void )
        : type( NONE )
    {}

    const char Str( void ) const {
        assert( this->type < COUNT );
        
        static const char s_piece[ COUNT ] = {
            '.', 'X', 'O'
        };
        return s_piece[ this->type ];
    }

    bool operator == (
        const Piece & piece
    ) {
        return this->type == piece.type;
    }

    bool operator != (
        const Piece & piece
    ) {
        return !( *this == piece );
    }

    EType operator ! ( void )
    {
        switch( this->type ) {
            case BLACK : return WHITE;
            case WHITE : return BLACK;
            case NONE  :
            default    : return NONE;
        }
    }
};

//===========================================================================
// enum ESide
//===========================================================================
enum ESide
{
    ES_BOTTOM = 0,
    ES_TOP    = 1,
};

//===========================================================================
// IPosition
//===========================================================================
struct Position
{
    unsigned int x;
    unsigned int y;

    static const unsigned int INVALID_COORDINATE;
    static const Position     INVALID_POSITION;

    Position(
        unsigned int x,
        unsigned int y
    )   : x( x )
        , y( y )
    {}      
    
    Position( void )
        : x( INVALID_COORDINATE )
        , y( INVALID_COORDINATE )
    {}

    Position(
        const Position & position
    )   : x( position.x )
        , y( position.y )
    {}

    bool operator == (
        const Position & position
    ) const {
        return this->x == position.x
            && this->y == position.y;
    }
};

//===========================================================================
// IRoute
//===========================================================================
interface IRoute
{
    virtual Piece GetPiece( void ) const = 0;
    
    virtual unsigned int GetMoveCount( void ) const = 0;
    
    virtual const Position * GetAt( unsigned int index ) const = 0;

    virtual const Position * GetDestination( void ) const = 0;

    virtual void RewindLastMoves( unsigned int count ) = 0;

    virtual bool HasJumped( void ) const = 0;
    
    virtual void AppendMove( const Position & position ) = 0;

    virtual void AppendMove( const IRoute * route ) = 0;
    
    virtual void Cleanup( void ) = 0;
    
    virtual void Release( void ) = 0;

    virtual IRoute * Clone( void ) = 0;
};

//===========================================================================
IRoute * CreateRoute( Piece piece = Piece::NONE );

//===========================================================================
// IBoard
//===========================================================================
interface IBoard
{
    virtual void Initialize(
        Piece top,
        Piece bottom
    ) = 0;

    virtual void Release( void ) = 0;

    virtual void Draw( void ) const = 0;
    
    virtual bool Move( const IRoute * route ) = 0;

    virtual unsigned int GetSize( void ) const = 0;

    virtual bool IsValidPosition(
        const Position * position
    ) const = 0;

    virtual bool GetPiece(
        const Position * position,
              Piece &    piece
    ) const = 0;
};

//===========================================================================
IBoard * CreateBoard( void );

//===========================================================================
// IBoard
//===========================================================================
interface IPlayer
{
    virtual bool OnTurn( void ) = 0;

    virtual void Release( void ) = 0;

    virtual Piece GetPieceType( void ) const = 0;
    
    virtual ESide GetSide( void ) const = 0;
};

//===========================================================================
void CreatePlayers(
    IBoard *   board,
    IPlayer *& human,
    IPlayer *& ai
);

//===========================================================================
interface IGameController
{
    virtual void Initialize( void ) = 0;

    virtual void Play( void ) = 0;

    virtual void Release( void ) = 0;
};

//===========================================================================
IGameController * CreateGameController( void );

#endif // INTERFACE_H